package service

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/elastic/go-elasticsearch/v7/esapi"
	"gorm.io/gorm"
	"log"
	"strconv"
	"time"
	"video-service/basic/global"
	__ "video-service/basic/proto"
	"video-service/internal/model"
	"video-service/pkg"
)

type VideoService struct {
	__.UnimplementedVideoServer
}

func (v *VideoService) VideoAdd(_ context.Context, req *__.VideoAddReq) (resp *__.VideoAddResp, err error) {
	video := model.Video{
		Title:     req.Title,
		Types:     req.Types,
		Category:  req.Category,
		Area:      req.Area,
		Year:      req.Year,
		CoverURL:  req.CoverUrl,
		Introduce: req.Introduce,
	}

	err = global.DB.Debug().Create(&video).Error
	if err != nil {
		return nil, err
	}

	resp = &__.VideoAddResp{
		Id: int64(video.ID),
	}

	return resp, err
}

func (v *VideoService) AuditVideo(_ context.Context, req *__.AuditVideoReq) (resp *__.AuditVideoResp, err error) {

	video := model.Video{}

	global.DB.Debug().Where("id = ?", req.VideoId).First(&video)

	if video.ID == 0 {
		return nil, fmt.Errorf("当前视频不存在")
	}

	texts := map[string]string{
		"title":     video.Title,
		"introduce": video.Introduce,
	}

	pass, reason := pkg.VideoReview(texts, video.CoverURL)

	if !pass {
		err = global.DB.Model(&video).Updates(map[string]interface{}{
			"status":        2,      // 2 = 驳回
			"reject_reason": reason, // 记录驳回理由
		}).Error

		if err != nil {
			return nil, fmt.Errorf("更新驳回状态失败: %v", err)
		}
		return &__.AuditVideoResp{
			Success: false,
			Message: "视频审核不通过",
		}, nil
	}

	// 4. 审核通过 → 状态改为通过
	err = global.DB.Model(&video).Updates(map[string]interface{}{
		"status":        1,          // 1 = 通过
		"reject_reason": "审核通过", // 清空驳回理由
	}).Error
	if err != nil {
		return nil, fmt.Errorf("更新审核状态失败: %v", err)
	}

	// 5. 返回结果
	return &__.AuditVideoResp{
		Success: true,
		Message: "视频审核通过",
	}, nil

}

func (v *VideoService) VideoList(_ context.Context, req *__.VideoListReq) (resp *__.VideoListResp, err error) {

	cacheKey := "video_list"

	// 试从 Redis 读取
	val, err := global.Rdb.Get(global.Ctx, cacheKey).Result()
	if err == nil && val != "" {
		// Redis 有缓存，直接反序列化返回
		var videoList []*__.VideoItem
		err = json.Unmarshal([]byte(val), &videoList)
		if err == nil {
			return &__.VideoListResp{
				Videos: nil,
			}, nil
		}
		// 反序列化失败，继续从 DB 查询
	}

	var videos []model.Video

	tx := global.DB.Debug().Model(&videos)

	if req.Area != "" {
		tx = tx.Where("area = ?", req.Area)
	}

	if req.Types != "" {
		tx = tx.Where("types = ?", req.Types)
	}

	if req.Category != "" {
		tx = tx.Where("category = ?", req.Category)
	}

	tx.Scopes(Paginate(int(req.Page), int(req.Size))).Find(&videos)

	var videoList []*__.VideoItem

	for _, video := range videos {
		list := &__.VideoItem{
			Id:        int64(video.ID),
			Title:     video.Title,
			CoverUrl:  video.CoverURL,
			Types:     video.Types,
			Category:  video.Category,
			PlayCount: video.PlayCount,
			Score:     video.Score,
			Area:      video.Area,
			Year:      video.Year,
		}

		videoList = append(videoList, list)
	}
	resp = &__.VideoListResp{
		Videos: videoList,
	}

	// 写入 Redis，设置过期时间（例如 5 分钟）
	data, _ := json.Marshal(videoList)
	global.Rdb.Set(global.Ctx, cacheKey, data, 5*time.Minute)

	return resp, nil

}

func Paginate(page int, size int) func(db *gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {

		if page <= 0 {
			page = 1
		}

		switch {
		case size > 100:
			size = 100
		case size <= 0:
			size = 10
		}

		offset := (page - 1) * size
		return db.Offset(offset).Limit(size)
	}
}

func (v *VideoService) VideoDetail(_ context.Context, req *__.VideoDetailReq) (resp *__.VideoDetailResp, err error) {

	var videos []model.Video

	global.DB.Debug().Where("id = ?", req.Id).Find(&videos)

	var videoList []*__.VideoItem

	for _, video := range videos {
		list := &__.VideoItem{
			Id:        int64(video.ID),
			Title:     video.Title,
			CoverUrl:  video.CoverURL,
			Types:     video.Types,
			Category:  video.Category,
			PlayCount: video.PlayCount,
			Score:     video.Score,
			Area:      video.Area,
			Year:      video.Year,
			Actors:    video.Actors,
		}

		videoList = append(videoList, list)
	}
	resp = &__.VideoDetailResp{
		Videos: videoList,
	}

	return resp, nil

}

func (v *VideoService) AddEpisode(_ context.Context, req *__.AddEpisodeRequest) (resp *__.AddEpisodeResponse, err error) {
	episode := model.VideoEpisode{
		VideoID:    req.VideoId,
		Title:      req.Title,
		EpisodeNum: req.EpisodeNumber,
		PlayURL:    req.EpisodeUrl,
		Duration:   req.Duration,
		PlayCount:  0,
		Status:     0,
	}

	err = global.DB.Debug().Create(&episode).Error
	if err != nil {
		return nil, err
	}

	resp = &__.AddEpisodeResponse{
		Id: int64(episode.ID),
	}

	return resp, err
}

func (v *VideoService) EpisodeList(_ context.Context, req *__.EpisodeListRequest) (resp *__.EpisodeListResponse, err error) {
	var episodes []model.VideoEpisode

	err = global.DB.Debug().Where("video_id = ?", req.VideoId).Find(&episodes).Error

	if err != nil {
		return nil, err
	}

	var episodeList []*__.VideoEpisode
	for _, video := range episodes {
		list := &__.VideoEpisode{
			Id:            int64(video.ID),
			VideoId:       video.VideoID,
			Title:         video.Title,
			EpisodeUrl:    video.PlayURL,
			Duration:      int32(video.Duration),
			PlayCount:     video.PlayCount,
			EpisodeNumber: int32(video.EpisodeNum),
		}
		episodeList = append(episodeList, list)
	}

	resp = &__.EpisodeListResponse{List: episodeList}
	return resp, err

}

func (v *VideoService) UpdatePlayLog(_ context.Context, req *__.PlayLogReq) (resp *__.PlayLogResp, err error) {
	var log model.PlayLog

	// 判断是否存在记录
	err = global.DB.Debug().Where("user_id = ? AND episode_id = ?", req.UserId, req.EpisodeId).First(&log).Error
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}

	if log.ID == 0 {
		// 不存在 -> 创建新记录
		log = model.PlayLog{
			UserId:        req.UserId,
			EpisodeId:     req.EpisodeId,
			PlayProgress:  float64(req.PlayProgress),
			WatchDuration: req.WatchDuration,
			IsComplete:    req.IsComplete,
		}
		err = global.DB.Debug().Create(&log).Error
	} else {
		// 存在 -> 更新记录
		err = global.DB.Model(&model.PlayLog{}).
			Where("id = ?", log.ID).
			Updates(map[string]interface{}{
				"play_progress":  req.PlayProgress,
				"watch_duration": req.WatchDuration,
				"is_complete":    req.IsComplete,
			}).Error
	}

	if err != nil {
		return nil, err
	}

	return &__.PlayLogResp{Message: "播放记录更新成功"}, nil
}
func (v *VideoService) GetPlayLogs(_ context.Context, req *__.PlayLogRequest) (resp *__.PlayLogResponse, err error) {
	var playLogs []model.PlayLog

	// 查询播放记录，按时间倒序排序（CreatedAt）
	err = global.DB.Debug().Where("user_id = ?", req.UserId).Order("created_at desc").Find(&playLogs).Error

	if err != nil {
		return nil, err
	}

	var playLogList []*__.PlayLogItem
	for _, log := range playLogs {
		list := &__.PlayLogItem{
			Id:            int64(log.ID),
			UserId:        log.UserId,
			EpisodeId:     log.EpisodeId,
			PlayProgress:  float32(log.PlayProgress),
			WatchDuration: log.WatchDuration,
			IsComplete:    log.IsComplete,
		}
		playLogList = append(playLogList, list)
	}

	resp = &__.PlayLogResponse{
		PlayLogs: playLogList,
	}

	return resp, nil
}

func (v *VideoService) GetTop50Videos(_ context.Context, req *__.TopVideosRequest) (resp *__.TopVideosResponse, err error) {
	// 创建一个上下文

	key := "video_hotness"

	// 获取 Redis 中的前 50 个视频，按热度倒序排列
	ids, err := global.Rdb.ZRevRange(global.Ctx, key, 0, 49).Result()
	if err != nil {
		return nil, fmt.Errorf("获取排行榜失败: %v", err)
	}

	// 创建一个返回的 TopVideosResponse
	var topVideos []*__.VideoItems

	// 遍历视频 ID，并查询数据库
	for _, id := range ids {
		video := GetVideoByID(id) // 你需要实现这个查询方法
		if video != nil {
			topVideos = append(topVideos, &__.VideoItems{
				Id:        int64(video.ID),
				Title:     video.Title,
				CoverUrl:  video.CoverURL,
				PlayCount: video.PlayCount,
				Hot:       float32(video.Hot),
			})
		}
	}

	// 构造 proto 返回结构
	return &__.TopVideosResponse{
		TopVideos: topVideos,
	}, nil
}
func GetVideoByID(videoId string) *model.Video {
	// 根据视频 ID 查询数据库，返回视频信息
	// 假设你有一个 `model.Video` 类型的查询方法
	video := model.Video{}

	err := global.DB.Debug().Where("id = ?", videoId).First(&video).Error
	if err != nil {
		fmt.Println("未找到该视频")
		return nil
	}
	return &video
}
func (v *VideoService) SyncVideo(_ context.Context, in *__.SyncVideoReq) (resp *__.SyncVideoResp, err error) {

	var video model.Video

	global.DB.Debug().Where("id = ?", in.VideoId).First(&video)

	// Build the request body.
	data, err := json.Marshal(video)
	if err != nil {
		log.Fatalf("Error marshaling document: %s", err)
	}

	// Set up the request object.
	req := esapi.IndexRequest{
		Index:      "video",
		DocumentID: strconv.Itoa(int(video.ID)),
		Body:       bytes.NewReader(data),
		Refresh:    "true",
	}

	// Perform the request with the client.
	res, err := req.Do(context.Background(), global.ElasticSearchClient)
	if err != nil {
		log.Fatalf("Error getting response: %s", err)
	}
	defer res.Body.Close()
	resp = &__.SyncVideoResp{}
	return resp, nil
}
func (v *VideoService) SearchVideo(_ context.Context, in *__.SearchVideoReq) (resp *__.SearchVideoResp, err error) {
	keyword := in.Keyword

	var buf bytes.Buffer
	query := map[string]interface{}{
		"query": map[string]interface{}{
			"match": map[string]interface{}{
				"Title": keyword,
			},
		},
	}
	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		log.Fatalf("Error encoding query: %s", err)
	}

	es := global.ElasticSearchClient
	// Perform the search request.
	res, err := es.Search(
		es.Search.WithContext(context.Background()),
		es.Search.WithIndex("video"),
		es.Search.WithBody(&buf),
		es.Search.WithTrackTotalHits(true),
		es.Search.WithPretty(),
	)
	if err != nil {
		log.Fatalf("Error getting response: %s", err)
	}
	defer res.Body.Close()
	var r map[string]interface{}
	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
		log.Fatalf("Error parsing the response body: %s", err)
	}

	fmt.Println(r)
	var list []*__.VideoItemss

	for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
		//fmt.Println(hit.(map[string]interface{})["_source"])
		source := hit.(map[string]interface{})["_source"].(map[string]interface{})
		list = append(list, &__.VideoItemss{
			Id:    uint64(source["ID"].(float64)),
			Title: source["Title"].(string),
		})
	}

	return &__.SearchVideoResp{
		VideoList: list,
	}, nil
}
